package br.com.ia.controle;

import br.com.ia.bean.Host;
import br.com.ia.bean.Aresta;
import br.com.ia.bean.Rede;
import java.awt.Color;
import java.util.HashMap;
import java.util.Iterator;
import javax.swing.JFrame;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.action.layout.graph.NodeLinkTreeLayout;
import prefuse.controls.DragControl;
import prefuse.controls.NeighborHighlightControl;
import prefuse.controls.PanControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Table;
import prefuse.data.expression.AndPredicate;
import prefuse.data.expression.Predicate;
import prefuse.data.expression.parser.ExpressionParser;
import prefuse.render.AbstractShapeRenderer;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.EdgeRenderer;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;

/**
 *
 * @author Jonas Cezar
 */
public class GeraGrafo {

    private final String graph = "graph";
    private final String nodes = "graph.nodes";
    private final String edges = "graph.edges";
    public final static int STATUS_NONE = 0;
    public final static int STATUS_NODE_INICIAL = 1;
    public final static int STATUS_NODE_FINAL = 2;
    public final static int STATUS_NODE_VISITADO = 3;
    public final static int STATUS_EDGE_ROTA = 1;
    private static HashMap<String, Node> nodeList;
    private static Graph myGraph;
    private static Visualization vis;
    final AndPredicate filter = new AndPredicate();
    final Predicate predicateColorNone = ExpressionParser.predicate("status = " + STATUS_NONE);
    final Predicate predicateColorNodeInicial = ExpressionParser.predicate("status = " + STATUS_NODE_INICIAL);
    final Predicate predicateColorNodoFinal = ExpressionParser.predicate("status = " + STATUS_NODE_FINAL);
    final Predicate predicateColorNodeVisitado = ExpressionParser.predicate("status = " + STATUS_NODE_VISITADO);
    final Predicate predicateColorEdgeRota = ExpressionParser.predicate("status = " + STATUS_EDGE_ROTA);

    public void gerar(Rede rede) {

        Table table = new Table();
        table.addColumn("id", int.class);
        table.addColumn("rotulo", String.class);
        table.addColumn("ip", String.class);
        table.addColumn("status", int.class);

        Table tedges = new Table();
        tedges.addColumn("id1", int.class);
        tedges.addColumn("id2", int.class);
        tedges.addColumn("custo", int.class);
        tedges.addColumn("status", int.class);

        myGraph = new Graph(table, tedges, true, "id1", "id2");

        nodeList = new HashMap<String, Node>();

        Iterator i = rede.getHosts().keySet().iterator();

        while (i.hasNext()) {

            Host host = (Host) rede.getHosts().get((String) i.next());

            Node t = nodeList.get(host.getRotulo());

            if (t == null) {
                t = createNode(myGraph, host);
                nodeList.put(host.getRotulo(), t);
            }

            for (Aresta aresta : host.getArestas()) {

                Node s = nodeList.get(aresta.getHost().getRotulo());

                if (s == null) {
                    s = createNode(myGraph, aresta.getHost());
                    nodeList.put(aresta.getHost().getRotulo(), s);
                }

                if (myGraph.getEdge(s, t) == null) {
                    myGraph.addEdge(t, s).set("status", STATUS_NONE);
                }
            }
        }


        vis = new Visualization();
        vis.add(graph, myGraph);


        LabelRenderer r = new LabelRenderer("rotulo");
        r.setRoundedCorner(8, 8); // round the corners
        r.setRenderType(AbstractShapeRenderer.RENDER_TYPE_FILL);
        r.setHorizontalAlignment(Constants.CENTER);

        EdgeRenderer c = new EdgeRenderer(Constants.EDGE_TYPE_LINE);
        c.setDefaultLineWidth(2);
        c.setArrowHeadSize(0, 0);
        c.setRenderType(AbstractShapeRenderer.RENDER_TYPE_DRAW);

        vis.setRendererFactory(new DefaultRendererFactory(r, c));

        ColorAction fillNodes = new ColorAction(nodes,
                VisualItem.FILLCOLOR, ColorLib.rgb(0, 200, 255));

        fillNodes.add(predicateColorNone, ColorLib.rgb(200, 200, 200));
        fillNodes.add(predicateColorNodeInicial, ColorLib.rgb(102,204,51));
        fillNodes.add(predicateColorNodoFinal, ColorLib.rgb(255,51,0));
        fillNodes.add(predicateColorNodeVisitado, ColorLib.rgb(102,153,204));

        ColorAction fillEdges = new ColorAction(edges, VisualItem.STROKECOLOR, ColorLib.rgb(200, 200, 200));
        fillEdges.add(predicateColorNone, ColorLib.rgb(200, 200, 200));
        fillEdges.add(predicateColorEdgeRota, ColorLib.rgb(255,51,0));

        ActionList draw = new ActionList();
        draw.add(fillNodes);
        draw.add(fillEdges);
        draw.add(new ColorAction(nodes, VisualItem.STROKECOLOR, 0));
        draw.add(new ColorAction(nodes, VisualItem.TEXTCOLOR, ColorLib.rgb(0, 0, 0)));

        ActionList layout = new ActionList(ActionList.INFINITY);
        layout.add(new ForceDirectedLayout(graph, false));
        layout.add(new RepaintAction());

        // create the layout action for the graph
        NodeLinkTreeLayout treeLayout = new NodeLinkTreeLayout(graph);
        treeLayout.setOrientation(0);

        // add the actions to the visualization
        vis.putAction("treeLayout", treeLayout);
        vis.putAction("draw", draw);
        vis.putAction("layout", layout);

        Display display = new Display(vis);
        display.setSize(400, 400); // Tamanho do display
        display.pan(200, 200); // Configura o centro do Display.
        display.setForeground(Color.GRAY);
        display.setBackground(Color.WHITE);
        display.addControlListener(new DragControl()); // drag items around
        display.addControlListener(new PanControl());  // pan with background left-drag
        display.addControlListener(new ZoomControl()); // zoom with vertical right-drag
        display.addControlListener(new WheelZoomControl());
        display.addControlListener(new ZoomToFitControl());
        display.addControlListener(new NeighborHighlightControl());

        JFrame frame = new JFrame("Trabalho IA on Prefuse");
        // ensure application exits when window is closed
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(display);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        vis.run("treeLayout");
        vis.run("draw");  // assign the colors
        vis.run("layout"); // start up the animated layout

    }

    public Node createNode(Graph g, Host host) {
        Node n = g.addNode();
        n.set("id", host.hashCode());
        n.set("rotulo", host.getRotulo());
        n.set("ip", host.getIp());
        n.set("status", STATUS_NONE);
        return n;
    }

    public static void setNodeStatus(String rotulo, int statu) {

        if (nodeList != null && myGraph != null) {

            Node node = nodeList.get(rotulo);

            if (node != null) {
                node.set("status", statu);
                vis.run("draw");
            }
        }
    }

    public static void setEdgeStatus(String rotulo1, String rotulo2, int statu) {

        if (nodeList != null && myGraph != null) {

            Node node1 = nodeList.get(rotulo1);
            Node node2 = nodeList.get(rotulo2);

            if (node1 != null && node2 != null) {
                Edge edge = myGraph.getEdge(node1, node2);
                if (edge == null) {
                    edge = myGraph.getEdge(node2, node1);
                    if (edge != null) {
                        edge.set("status", statu);
                        vis.run("draw");
                    }
                } else {
                    edge.set("status", statu);
                    vis.run("draw");
                }
            }
        }
    }

    public static void zeraStatus() {

        Iterator i = myGraph.nodes();

        while (i.hasNext()) {

            Node node = (Node) i.next();

            if (node != null) {
                node.set("status", STATUS_NONE);
            }
        }

        Iterator j = myGraph.edges();

        while (j.hasNext()) {

            Edge edge = (Edge) j.next();

            if (edge != null) {
                edge.set("status", STATUS_NONE);
            }
        }

        vis.run("draw");
    }
}
